#include "asyncfilebuffer.h"
#include <QMutexLocker>
#include <QDebug>

FbCommand::FbCommand() :
    type(NOP)
{}

FbCommand::Type FbCommand::getType()
{
    return type;
}

QString FbCommand::getFileName()
{
    return fileName;
}

void FbCommand::set(Type type, QString fileName = QString())
{
    this->type = type;
    this->fileName = fileName;
}

void FbCommand::reset()
{
    type = NOP;
    fileName.clear();
}

AsyncFileBuffer::AsyncFileBuffer() :
    isRunning(false)
{           
    worker = new QThreadPool(this);
    worker->setMaxThreadCount(1);
    setAutoDelete(false);
}

AsyncFileBuffer::~AsyncFileBuffer()
{
    delete worker;
}

QString AsyncFileBuffer::holdLoad()
{
    if(hold()){
        return QString();
    } else {
        return tr("Не могу загрузить файл: обслуживающий поток занят");
    }
}

void AsyncFileBuffer::loadFile(QString fName)
{
//    QString holdError = holdLoad();
//    if(holdError.isNull()){
        cmd.set(FbCommand::LOAD, fName);
        worker->start(this, QThread::LowestPriority);
        cmdGuard.unlock();
//        return;
//    } else {
//        qCritical() << holdError;
////        emit fileLoadingError(fName, errorString, getState());
//        return;
//    }
}

QString AsyncFileBuffer::holdSave()
{
    if(hold()){
        QString errorString = FileBufferWithSignals::holdSave();
        if(!errorString.isNull()){
            cmdGuard.unlock();
        }
        return errorString;
    } else {
        return tr("Не могу сохранить файл: обслуживающий поток занят");
    }
}

void AsyncFileBuffer::saveFile(QString fName)
{
//    QString holdError = holdSave();
//    if(holdError.isNull()){
        cmd.set(FbCommand::SAVE, fName);
        worker->start(this, QThread::LowestPriority);
        cmdGuard.unlock();
//        return;
//    } else {
//        qCritical() << holdError;
////        emit fileSavingError(fName, errorString);
//        return;
//    }
}

void AsyncFileBuffer::release()
{
    cmdGuard.unlock();
}

QString AsyncFileBuffer::holdClear()
{
    if(hold()){
        QString errorString = FileBuffer::holdClear();
        if(!errorString.isNull()){
            cmdGuard.unlock();
        }
        return errorString;
    } else {
        return tr("Не могу очистить память: обслуживающий поток занят");
    }
}

void AsyncFileBuffer::clearData()
{
    //! тредобезопасность не очевидна, но при использовании holdClear можно показать, что безопасно
    if(isRunning){
        FileBufferWithSignals::clearData();
    } else {
        cmd.set(FbCommand::CLEAR);
        worker->start(this, QThread::LowestPriority);
        cmdGuard.unlock();
    }
}

QString AsyncFileBuffer::getLastError()
{
    QMutexLocker runGuardLocker(&runGuard);
    return FileBufferWithSignals::getLastError();
}

IFileBuffer::State AsyncFileBuffer::getState()
{
    QMutexLocker stateGuardLocker(&stateGuard);
    return FileBufferWithSignals::getState();
}

void AsyncFileBuffer::setState(IFileBuffer::State newState)
{
    QMutexLocker stateGuardLocker(&stateGuard);
    FileBufferWithSignals::setState(newState);
}

QString AsyncFileBuffer::getFileName()
{
    QMutexLocker locker(&fileNameGuard);
    return FileBufferWithSignals::getFileName();
}

void AsyncFileBuffer::setFileName(QString newFileName)
{
    QMutexLocker locker(&fileNameGuard);
    FileBufferWithSignals::setFileName(newFileName);
}

qint64 AsyncFileBuffer::getFileSize()
{
    QMutexLocker locker(&fileSizeGuard);
    return FileBufferWithSignals::getFileSize();
}

void AsyncFileBuffer::setFileSize(qint64 newSize)
{
    QMutexLocker locker(&fileSizeGuard);
    FileBufferWithSignals::setFileSize(newSize);
}

qint64 AsyncFileBuffer::getBytesProcessed()
{
    QMutexLocker locker(&bytesProcessedGuard);
    return FileBufferWithSignals::getBytesProcessed();
}

void AsyncFileBuffer::setBytesProcessed(qint64 newSize)
{
    QMutexLocker locker(&bytesProcessedGuard);
    FileBufferWithSignals::setBytesProcessed(newSize);
}

void AsyncFileBuffer::incBytesProcessed(qint64 incSize)
{
    QMutexLocker locker(&bytesProcessedGuard);
    FileBufferWithSignals::incBytesProcessed(incSize);
}

void AsyncFileBuffer::trigger()
{
    QMutexLocker locker(&workStatusGuard);
    switch (workStatus){
    case PAUSED:
        workStatus = OK;
        workStatusDefined.wakeAll();
        break;
    case OK:
        workStatus = PAUSED;
        break;
    default:
        break;
    }
}

void AsyncFileBuffer::stop()
{
    QMutexLocker locker(&workStatusGuard);
    workStatus = CANCELLED;
    workStatusDefined.wakeAll();
}

void AsyncFileBuffer::enableWork()
{
    QMutexLocker locker(&workStatusGuard);
    workStatus = OK;
}

bool AsyncFileBuffer::canWork()
{
    QMutexLocker locker(&workStatusGuard);
    if(workStatus == PAUSED){
        emit opPaused();
        while(workStatus == PAUSED){
            workStatusDefined.wait(&workStatusGuard);
        }
        if(workStatus == OK){
            emit opResumed();
            return true;
        } else {
            return false;
        }
    }
    return (workStatus == OK);
}

bool AsyncFileBuffer::hold()
{
    bool success = false;
    if(cmdGuard.tryLock()){
        if(cmd.getType() == FbCommand::NOP){
            success = true;
        } else {
            cmdGuard.unlock();
        }
    }
    return success;
}

void AsyncFileBuffer::run()
{
    QMutexLocker cmdGuardLocker(&cmdGuard);
    QMutexLocker runGuardLocker(&runGuard);
    isRunning = true;
    switch (cmd.getType())
    {
        case FbCommand::LOAD:
            FileBufferWithSignals::loadFile(cmd.getFileName());
            break;
        case FbCommand::SAVE:
            FileBufferWithSignals::saveFile(cmd.getFileName());
            break;
        case FbCommand::CLEAR:
            FileBufferWithSignals::clearData();
            break;
        default:
            break;
    }
    cmd.reset();
    isRunning = false;
}
